Skip to content

feat(tbor): implement HKDF and single-step (X9.63 / SP 800-56A) KDF derivation - #589

Merged
Jayant Gandhi (jaygmsft) merged 5 commits into
mainfrom
tbor/kdf-crypto
Aug 4, 2026
Merged

feat(tbor): implement HKDF and single-step (X9.63 / SP 800-56A) KDF derivation#589
Jayant Gandhi (jaygmsft) merged 5 commits into
mainfrom
tbor/kdf-crypto

Conversation

@vsonims

Copy link
Copy Markdown
Collaborator

Summary

Adds two in-session TBOR KDF commands that derive key material from a caller-held masked ECDH shared secret (from EcdhDerive 0x19) and return the derived key masked under the requested scope — the KDF analogues of the ECC crypto commands, re-masking the output instead of vaulting it.

Opcode Command KDF
0x1C HkdfDerive HKDF (RFC 5869), salt + info
0x1D ConcatKdfDerive single-step: ANSI X9.63 or NIST SP 800-56A one-step (selected by kdf_alg), single info

Both derive AES (128/192/256) or HMAC (fixed SHA-256/384/512, or variable-length) keys.

Design

  • Unmask-on-use: the ECDH secret is unmasked in place in the request buffer, checked to be an ECDH-secret kind carrying derive, run through the KDF into scratch, and the output masked under the target scope. The recovered secret and derived scratch are scrubbed on every path.
  • Shared KdfKeyType output-type enum lives in key_props (used by both commands).
  • Cross-platform single-step KDF in azihsm_crypto: X9.63 and SP 800-56A are Hash-only, so one generic ConcatKdfAlgo over the shared Hasher serves both OpenSSL (Linux) and CNG (Windows) — no per-backend code. The std PAL x963_kdf / sp800_56a_kdf hooks (previously todo!() stubs) now delegate to it via StdKdf::concat_kdf; the Uno PAL already implemented them.

Changes

  • azihsm_crypto: ConcatKdfAlgo + ConcatKdfMode {X963, Sp800_56a} (crates/crypto/src/kdf/concat.rs), 3 CryptoError variants, known-answer tests (kdf/tests/concat_tests.rs).
  • std PAL: StdKdf::concat_kdf + wired both PAL methods; HsmError::ConcatKdfError.
  • TBOR: wire schemas (fw + host mirror), handlers, dispatch + 3 classifiers + op.rs for 0x1C / 0x1D.
  • Tests: emu coverage for every output key type under each KDF (and each X9.63 / SP 800-56A variant), all hashes/scopes, optional salt/info, and rejects (unknown hash / KDF variant / key type, var-HMAC missing / out-of-range length, non-ECDH-secret IKM).
  • Docs: hkdf_derive.md, concat_kdf_derive.md, README rows.

Testing

  • azihsm_crypto: 501 lib tests pass (incl. X9.63 / SP 800-56A KATs vs. independently computed vectors).
  • Full TBOR emu suite: 146 pass (19 new).
  • cargo check fw core (host + Uno no_std) + Uno PAL ✅ · clippy (crypto / std-PAL / fw / host) ✅ · nightly fmt ✅ · copyright ✅.

Notes

  • Based on feat(tbor): implement ECC keygen, sign, and ECDH derive #585 (tbor/ecc-crypto) for the ECDH shared-secret input. Uses opcodes 0x1C / 0x1D after the sibling AES / ECC / RSA / Hash crypto branches (0x15..0x1B).
  • ConcatKdfDerive is TBOR-only (no MBOR analogue). The concat KDFs' byte-level correctness is proven by the azihsm_crypto KATs; the emu tests verify command plumbing (the derived key is only observable masked).

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Add three stateless masked-key TBOR crypto commands mirroring the MBOR
vault-key-handle equivalents:

- EccGenerateKey (0x17): generate an ECC keypair (P-256/384/521) and
  return the private key AEAD-GCM-256 masked under the requested scope
  plus the wire public key. Nothing is persisted on-device.
- EccSign (0x18): unmask a caller-held masked ECC private key in place,
  ECDSA-sign a host-supplied pre-hashed digest, return raw r||s.
- EcdhDerive (0x19): unmask a masked local ECC private key in place,
  derive an ECDH shared secret against a host peer public key, and
  re-mask the secret under a target scope.

Uses the zero-copy reserve+fill encoder and in-place unmask (decode_mut)
patterns. Recovered plaintext keys and derived secrets are scrubbed on
every return path (scope exit only resets the bump watermark). ECC import
is provided by the existing UnwrapKey (Ecc class); a cross-command test
covers UnwrapKey-Ecc -> EccSign.

Shared curve/kind mappings added to tbor from_pal (ecc_private,
ecdh_secret, ecc_private_curve); dispatch/classifier wiring in mod.rs and
op.rs. Wire schemas (fw + host mirror), emu integration tests (host-
verified ECDSA roundtrip for all curves), and per-command docs included.

Also harden the pre-existing RSA OAEP test
decrypt_rejects_corrupted_oaep_ciphertext: corrupting the most-significant
ciphertext byte can push the integer past the modulus, which OpenSSL reports
as INTERNAL_ERROR but CNG (Windows) reports as DDI_CMD_FAILURE. Accept either
rejection code so the platform-flaky test passes on Windows CI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
…erivation

Add two in-session TBOR KDF commands that derive key material from a
caller-held masked ECDH shared secret (from EcdhDerive) and return the
derived key masked under the requested scope — the KDF analogues of the
ECC crypto commands, re-masking the output instead of vaulting it:

- HkdfDerive (0x1C): HKDF (RFC 5869) with optional salt + info.
- ConcatKdfDerive (0x1D): single-step "concatenation" KDF, selecting
  ANSI X9.63 (SEC 1 §3.6.1) or NIST SP 800-56A r3 one-step (§5.8.2.1)
  via a kdf_alg discriminant; both take a single info octet string.

Both handlers unmask the ECDH secret in place, verify it is an ECDH
shared-secret kind carrying `derive`, run the KDF into scratch, mask the
output (AES / fixed- or variable-length HMAC) under the target scope, and
scrub the recovered secret and derived scratch on every path. The shared
KdfKeyType output-type enum moves to key_props (used by both commands).

Single-step KDF primitive (azihsm_crypto): the X9.63 and SP 800-56A
concatenation KDFs are implemented once, generically, over the shared
Hasher — they need nothing but a hash, so a single platform-agnostic
ConcatKdfAlgo serves both the OpenSSL (Linux) and CNG (Windows) backends
(unlike HKDF, which is per-backend). Known-answer tests validate both
variants against independently computed vectors (SHA-256/384/512, single-
and multi-block, absent info). The std PAL x963_kdf / sp800_56a_kdf hooks
(previously todo!() stubs) now delegate to it via a new StdKdf::concat_kdf;
the Uno PAL already implemented them. Adds HsmError::ConcatKdfError and
three CryptoError variants.

Wire schemas (fw + host mirror), dispatch / classifier / op.rs wiring
(in-session, session-id cross-checked, default-PSK gated) for both
opcodes. Emu tests cover every output key type under each KDF (and each
X9.63 / SP 800-56A variant), all hashes and scopes, optional salt/info,
and rejection of unknown hash / KDF variant / key type, variable-length
HMAC without / out-of-range length, and a non-ECDH-secret IKM. Per-command
docs added.

Based on tbor/ecc-crypto for the ECDH shared-secret input (EcdhDerive);
uses opcodes 0x1C / 0x1D after the sibling AES / ECC / RSA / Hash crypto
branches (0x15..0x1B).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0b09e50a-a9be-4bae-b347-d42dc775a258
Comment thread fw/core/lib/src/ddi/tbor/hkdf_derive.rs
@jaygmsft
Jayant Gandhi (jaygmsft) changed the base branch from tbor/ecc-crypto to main August 3, 2026 23:28
@jaygmsft
Jayant Gandhi (jaygmsft) dismissed their stale review August 3, 2026 23:28

The base branch was changed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: af077e4d-51d9-45f3-8e20-ea7f68813d17
@jaygmsft

Copy link
Copy Markdown
Contributor

Copilot merge latest main

Co-authored-by: jaygmsft <22506014+jaygmsft@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 4, 2026 14:13

Copilot AI commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Copilot merge latest main

Merged latest main into the branch in bbfe8cb — clean merge (no conflicts); incoming changes were .github/workflows/codeql.yml and the two MBOR bk3 smoke tests.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds two new in-session TBOR KDF derivation commands (HkdfDerive and ConcatKdfDerive) that take a caller-held masked ECDH shared secret, derive symmetric key material (AES/HMAC), and return the result masked under a requested scope. It also introduces a cross-platform single-step concatenation KDF implementation in azihsm_crypto and wires the std PAL’s previously-stubbed X9.63 / SP 800-56A hooks to that implementation, with end-to-end emu coverage and command documentation.

Changes:

  • Implement ConcatKdfAlgo (+ mode selection) in azihsm_crypto with known-answer tests.
  • Add TBOR firmware handlers, wire schemas, dispatch/classifiers, and host-side wrappers for HkdfDerive (0x1C) and ConcatKdfDerive (0x1D).
  • Wire std PAL X9.63 / SP 800-56A KDF hooks and add emu integration tests + docs for both commands.

Reviewed changes

Copilot reviewed 26 out of 26 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
rust-toolchain.toml Adds rust-analyzer as a toolchain component.
fw/plat/std/pal/src/kdf.rs Wires std PAL x963_kdf / sp800_56a_kdf to the std KDF driver’s concat KDF implementation.
fw/plat/std/pal/src/drivers/kdf.rs Adds async StdKdf::concat_kdf using azihsm_crypto’s ConcatKdfAlgo.
fw/pal/traits/src/error.rs Introduces HsmError::ConcatKdfError for concat-KDF failures.
fw/core/lib/src/op.rs Marks opcodes 0x1C / 0x1D as InSession for session-flag validation.
fw/core/lib/src/ddi/tbor/mod.rs Adds opcodes, dispatch routing, and opcode classifiers for the new TBOR KDF commands.
fw/core/lib/src/ddi/tbor/hkdf_derive.rs Implements firmware TBOR HkdfDerive handler (unmask-in-place, derive, re-mask, scrub).
fw/core/lib/src/ddi/tbor/concat_kdf_derive.rs Implements firmware TBOR ConcatKdfDerive handler (X9.63 / SP 800-56A, re-mask, scrub).
fw/core/ddi/tbor/types/src/lib.rs Registers and re-exports the new TBOR KDF wire-schema modules.
fw/core/ddi/tbor/types/src/key_props.rs Adds shared KdfKeyType open-enum for KDF output type selection.
fw/core/ddi/tbor/types/src/hkdf_derive.rs Adds TBOR wire schema + round-trip tests for HkdfDerive (fw-side).
fw/core/ddi/tbor/types/src/concat_kdf_derive.rs Adds TBOR wire schema + round-trip tests for ConcatKdfDerive (fw-side).
docs/tbor-ddi/README.md Documents new opcodes in the TBOR DDI command table.
docs/tbor-ddi/commands/hkdf_derive.md Adds command documentation for HkdfDerive.
docs/tbor-ddi/commands/concat_kdf_derive.md Adds command documentation for ConcatKdfDerive.
ddi/tbor/types/tests/commands/mod.rs Registers new emu integration test modules.
ddi/tbor/types/tests/commands/hkdf_derive.rs Adds emu integration tests for HKDF derivation wiring, variants, scopes, and rejects.
ddi/tbor/types/tests/commands/concat_kdf_derive.rs Adds emu integration tests for concat-KDF derivation wiring, variants, scopes, and rejects.
ddi/tbor/types/src/lib.rs Registers and re-exports new host-side TBOR wrappers.
ddi/tbor/types/src/hkdf_derive.rs Adds host-side request/response wrapper + basic encoding test for HkdfDerive.
ddi/tbor/types/src/concat_kdf_derive.rs Adds host-side request/response wrapper + basic encoding test for ConcatKdfDerive.
crates/crypto/src/lib.rs Adds CryptoError variants for concat-KDF failures.
crates/crypto/src/kdf/tests/mod.rs Registers concat-KDF KAT module.
crates/crypto/src/kdf/tests/concat_tests.rs Adds known-answer tests validating X9.63/SP 800-56A ordering, counters, truncation, and rejects.
crates/crypto/src/kdf/mod.rs Wires ConcatKdfAlgo + re-exports ConcatKdfMode.
crates/crypto/src/kdf/concat.rs Implements platform-agnostic X9.63 / SP 800-56A one-step concatenation KDF.

Comment thread fw/plat/std/pal/src/drivers/kdf.rs
Comment thread fw/pal/traits/src/error.rs
@jaygmsft
Jayant Gandhi (jaygmsft) added this pull request to the merge queue Aug 4, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 4, 2026
@jaygmsft
Jayant Gandhi (jaygmsft) added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit c3feb39 Aug 4, 2026
34 of 36 checks passed
@jaygmsft
Jayant Gandhi (jaygmsft) deleted the tbor/kdf-crypto branch August 4, 2026 17:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants